/*******************************************************************
 *
 * Copyright (C), 2021-2022, Letstrong, All rights reserved.
 *
 * Author      : 
 * Create Time : 2020-12-17
 * Version     : v0.1
 * Description : 
 *******************************************************************/

/*----------------------------Head file----------------------------*/
#include "wdt.h"
#include "log.h"
#include "sys_task.h"
#include "user_config.h"
#include "app_store_mem.h"

#define APP_MEM_LOG_EN		1

#if APP_MEM_LOG_EN
#define APP_MEM_LOG			LOG_DEBUG
#else
#define APP_MEM_LOG(...)
#endif

/*----------------------------macro file---------------------------*/

/*----------------------------type define--------------------------*/

/*----------------------------var define---------------------------*/
static uint8_t store_mem_err_cnt = 0;

static index_data_t skip_index = 
{
	//No need to save
	.index_start_addr = SKIP_INDEX_SAVED_START_ADDR,
	.index_end_addr = SKIP_INDEX_SAVED_END_ADDR,
	.data_start_addr = SKIP_DATA_SAVED_START_ADDR,
	.data_end_addr = SKIP_DATA_SAVED_END_ADDR,
	.cache_max_size = 100*sizeof(save_skip_packet_t), //22400 bytes, need (7 * 0x1000).
	.addr_valid = false,
	.data_size = sizeof(save_skip_packet_t),
	.tmp_addr = SKIP_DATA_SAVED_START_ADDR,

	//Need to save
	.index_addr = SKIP_INDEX_SAVED_START_ADDR,
	.latest_addr = SKIP_DATA_SAVED_START_ADDR,
	.oldest_addr = SKIP_DATA_SAVED_START_ADDR,
};

/*-------------------------func declarative------------------------*/

/*-----------------------------------------------------------------*/

static void app_store_mem_error_handle(void)
{
	if(store_mem_err_cnt < 3) {
		store_mem_err_cnt++;
	}
	else {
		store_mem_err_cnt = 0;
		sys_task_evt_set(SYS_EVT_STORE_MEM_ERR_HDL);
	}
}

static bool update_index_addr(uint32_t locate_addr, index_data_t *s_addr)
{
	if(locate_addr < s_addr->index_start_addr || locate_addr > s_addr->index_end_addr) {
		LOG_ERROR("locate_index address error.\n");
		app_store_mem_error_handle();
		return false;
	}

	s_addr->index_addr = locate_addr;
	
	return true;
}

static bool update_latest_data_addr(uint32_t latest_addr, index_data_t *s_addr)
{
	if(latest_addr < s_addr->data_start_addr || latest_addr > s_addr->data_end_addr) {
		LOG_ERROR("latest_index address error: %x.\n", latest_addr);
		app_store_mem_error_handle();
		return false;
	}

	s_addr->latest_addr = latest_addr;
	
	return true;
}

static bool update_oldest_data_addr(uint32_t oldest_addr, index_data_t *s_addr, bool max_size_check)
{
	if(oldest_addr < s_addr->data_start_addr || oldest_addr > s_addr->data_end_addr) {
		LOG_ERROR("oldest_index address error: %x.\n", oldest_addr);
		app_store_mem_error_handle();
		return false;
	}

	if(max_size_check) {
		if((s_addr->latest_addr >= oldest_addr)) {
			if((s_addr->latest_addr - oldest_addr) >= s_addr->cache_max_size)
				s_addr->oldest_addr = s_addr->latest_addr - s_addr->cache_max_size;
			else
				s_addr->oldest_addr = oldest_addr;
			//APP_MEM_LOG("oldest_index address11: %x.\n", s_addr->oldest_addr);
		}
		else {
			if( ((s_addr->data_end_addr - oldest_addr) + (s_addr->latest_addr - s_addr->data_start_addr)) \
					>= s_addr->cache_max_size)
			{
				if((s_addr->latest_addr - s_addr->data_start_addr) == s_addr->cache_max_size) {
					s_addr->oldest_addr = s_addr->data_start_addr;
				}
				else {
					uint32_t rest = (s_addr->data_end_addr - s_addr->data_start_addr) % s_addr->data_size;
					s_addr->oldest_addr = (s_addr->data_end_addr - rest) - \
						(s_addr->cache_max_size - (s_addr->latest_addr - s_addr->data_start_addr));
				}
				//APP_MEM_LOG("oldest_index address22: %x.\n", s_addr->oldest_addr);
			}
			else
				s_addr->oldest_addr = oldest_addr;
		}
	}
	else
		s_addr->oldest_addr = oldest_addr;

	return true;
}

static bool update_tmp_data_addr(uint32_t tmp_addr, index_data_t *s_addr)
{
	if(tmp_addr < s_addr->data_start_addr || tmp_addr > s_addr->data_end_addr) {
		//APP_MEM_LOG("oldest_index address error: %x.\n", tmp_addr);
		app_store_mem_error_handle();
		return false;
	}

	s_addr->tmp_addr = tmp_addr;

	return true;
}

static bool init_valid_addr(index_data_t *s_addr)
{
	uint32_t i;
	uint8_t result = 0;
	//bool sector1_valid = false, sector2_valid = false;
	index_content_t index[2];

	s_addr->addr_valid = false;

	for(i=0;i<((s_addr->index_end_addr - s_addr->index_start_addr)/sizeof(index_content_t));i++) {
		hal_flash_read(s_addr->index_start_addr+(i*sizeof(index_content_t)), (uint8_t*)&index, sizeof(index));
		if(INDEX_HEADER == index[0].header && INDEX_HEADER != index[1].header) {
			if(i == (((s_addr->index_end_addr - s_addr->index_start_addr)/sizeof(index_content_t)) -1)) {
				result += update_index_addr(s_addr->index_start_addr, s_addr);
			}
			else {
				result += update_index_addr(s_addr->index_start_addr+ ((i+1)*sizeof(index_content_t)), s_addr);
			}
			result += update_latest_data_addr(index[0].latest_addr, s_addr);
			result += update_oldest_data_addr(index[0].oldest_addr, s_addr, false);
			if(result < 3) {
				APP_MEM_LOG("Invalid index: %d.\n", i);
				return false;
			}
			s_addr->addr_valid = true;
			return true;
		}
	}

	if(i >= (s_addr->index_end_addr - s_addr->index_start_addr)/sizeof(index_content_t)) { //Fresh flash
		update_index_addr(s_addr->index_start_addr, s_addr);
		update_latest_data_addr(s_addr->data_start_addr, s_addr);
		update_oldest_data_addr(s_addr->data_start_addr, s_addr, false);
		hal_flash_erase_sector(s_addr->data_start_addr);
		s_addr->addr_valid = true;
		APP_MEM_LOG("Fresh flash!\n");
	}

	//if(!s_addr->addr_valid) {
		//APP_MEM_LOG("index_addr is invalid.\n");
	//}
	//APP_MEM_LOG("i:%d \n", i);
	//APP_MEM_LOG("latest: %x, oldest: %x, locate: %x\n", s_addr->latest_addr, s_addr->oldest_addr, s_addr->index_addr);
	return true;
}

static bool save_data_index(index_data_t *s_addr)
{
	index_content_t index;
	uint32_t temp_idx = 0, border_addr = 0;

	if(!s_addr->addr_valid)
		return false;

	//Force to check and erase next sector.
	border_addr = ((s_addr->index_addr/FLASH_SECTOR_SIZE) + 1)*FLASH_SECTOR_SIZE;
	if((s_addr->index_addr < s_addr->index_end_addr)\
		&& (s_addr->index_addr + 2*sizeof(index) >= s_addr->index_end_addr))
	{
		hal_flash_erase_sector(s_addr->index_start_addr);
	}
	else if((s_addr->index_addr < border_addr) \
		&& ((s_addr->index_addr + sizeof(index)) >= border_addr))
	{
		hal_flash_erase_sector((((s_addr->index_addr/FLASH_SECTOR_SIZE) + 1)*FLASH_SECTOR_SIZE));
	}

	//check_nor_data_whole_size(oldest_data_index, latest_data_index);
	index.header = INDEX_HEADER;
	index.latest_addr = s_addr->latest_addr;
	index.oldest_addr = s_addr->oldest_addr;
	//APP_MEM_LOG("update latest: %x, oldest: %x\n", latest_data_index, oldest_data_index);
	hal_flash_write(s_addr->index_addr, (uint8_t*)&index, sizeof(index));

	temp_idx = s_addr->index_addr + sizeof(index);
	if((temp_idx + sizeof(index)) > s_addr->index_end_addr) {
		temp_idx = s_addr->index_start_addr;
		//APP_MEM_LOG("Next locate loop.\n");
	}
	update_index_addr(temp_idx, s_addr);
	APP_MEM_LOG("latest: %x, oldest: %x, index: %x.\n", s_addr->latest_addr, s_addr->oldest_addr, s_addr->index_addr);
	return true;
}


void pack_and_save_data(index_data_t *s_addr, uint8_t* data, uint16_t size)
{
	uint32_t address = 0, temp_idx = 0;

	if(!s_addr->addr_valid)
		return;

	//Check and force to erase nouse area.
	//We consider the max size.
	//Maybe ignore some last area, so calculate with 2*sizeof(Sports_t).
	if((s_addr->latest_addr <= s_addr->data_end_addr) \
		&& ((s_addr->latest_addr + 2*size) >= s_addr->data_end_addr))
	{
		hal_flash_erase_sector(s_addr->data_start_addr);
	}
	else if((s_addr->latest_addr % FLASH_SECTOR_SIZE >= (FLASH_SECTOR_SIZE - size)) \
		&& (((s_addr->latest_addr + size)%FLASH_SECTOR_SIZE) <= size))
	{
		address = (s_addr->latest_addr/FLASH_SECTOR_SIZE)*FLASH_SECTOR_SIZE + FLASH_SECTOR_SIZE;
		hal_flash_erase_sector(address);
	}

	data[0] = DATA_SAVE_HEAD1;
	data[1] = DATA_SAVE_HEAD2;
	data[2] = DATA_SAVE_HEAD3;

	hal_flash_write(s_addr->latest_addr, (uint8_t*)&data[0], size);
	temp_idx = s_addr->latest_addr + size;
	//We consider the max size, ignore the last not enough area.
	if((temp_idx + size) > s_addr->data_end_addr)
		temp_idx = s_addr->data_start_addr;
	if(false == update_latest_data_addr(temp_idx, s_addr)) {
		return;
	}
	if(false == update_oldest_data_addr(s_addr->oldest_addr, s_addr, true)) {
		return;
	}
	save_data_index(s_addr);
}


bool is_exist_unsent_data(index_data_t s_addr)
{
	//APP_MEM_LOG("Oldest: %x, Latest: %x\n", oldest_data_index, latest_data_index);
	if(s_addr.oldest_addr == s_addr.latest_addr)
		return false;
	else
		return true;
}

//TODO: The device may power down suddenly, and the packet is not valid.
//		So we need to abandon the packet if its crc verification can not pass.
static bool read_oldest_data(index_data_t *s_addr, uint8_t* r_data, uint16_t size, bool set_tmp_addr)
{
	save_common_head_t head;
	uint32_t temp_idx = 0;

	if(!s_addr->addr_valid)
		return false;
	
	if(true == set_tmp_addr) {
		s_addr->tmp_addr = s_addr->oldest_addr;
	}
	//APP_MEM_LOG("read addr: %x.\n", s_addr->tmp_addr);
	hal_flash_read(s_addr->tmp_addr, (uint8_t*)&head, sizeof(save_common_head_t));
	if(DATA_SAVE_HEAD1 != head.byte1 || DATA_SAVE_HEAD2 != head.byte2 || DATA_SAVE_HEAD3 != head.byte3) {
		LOG_ERROR("byte1: %x, byte2: %x, byte3: %x\n", head.byte1, head.byte2, head.byte3);
		app_store_mem_error_handle();
		return false;
	}

	hal_flash_read(s_addr->tmp_addr, r_data, size);
	//We consider the max size, ignore the last not enough area.
	temp_idx = s_addr->tmp_addr + size;
	if((temp_idx + size) > s_addr->data_end_addr)
		temp_idx = s_addr->data_start_addr;
	if(false == update_tmp_data_addr(temp_idx, s_addr))
		return false;

	return true;
}


bool read_latest_data(index_data_t *s_addr, uint8_t* r_data, uint16_t size)
{
	save_common_head_t head;
	uint32_t addr = s_addr->latest_addr - s_addr->data_size;

	if(!s_addr->addr_valid)
		return false;

	//STORE_LOG("read addr: %x.\n", s_addr->oldest_addr);
	hal_flash_read(addr, (uint8_t*)&head, sizeof(save_common_head_t));
	if(DATA_SAVE_HEAD1 != head.byte1 || DATA_SAVE_HEAD2 != head.byte2 || DATA_SAVE_HEAD3 != head.byte3) {
		//STORE_LOG("byte1: %x, byte2: %x, byte3: %x\n", head.byte1, head.byte2, head.byte3);
		app_store_mem_error_handle();
		return false;
	}

	hal_flash_read(addr, r_data, size);
	return true;
}


static bool abandon_data_have_sent(index_data_t *s_addr)
{
	if(s_addr->oldest_addr == s_addr->latest_addr)
		return true;

	s_addr->oldest_addr = s_addr->tmp_addr;

	if(!save_data_index(s_addr))
		return false;
	return true;
}


static uint32_t sizeof_data_need_to_be_sent(index_data_t *s_addr)
{
	uint32_t size = 0;

	if(!s_addr->addr_valid)
		return 0;

	if(s_addr->latest_addr >= s_addr->oldest_addr)
		size = (s_addr->latest_addr - s_addr->oldest_addr);
	else {
		uint32_t rest = (s_addr->data_end_addr - s_addr->data_start_addr) % s_addr->data_size;
		size = (s_addr->latest_addr - s_addr->data_start_addr) + (s_addr->data_end_addr - s_addr->oldest_addr - rest);
	}

	return size;
}


/*void app_mem_latest_skip_ret_read(uint8_t* pData, uint16_t size)
{
	save_skip_packet_t skip_pkt;
	
	if(size < sizeof(skip_pkt.jump))
		return;
	
	if(read_latest_data(&skip_index, (uint8_t*)&skip_pkt, sizeof(skip_pkt))) {
		memcpy(pData, (uint8_t*)&skip_pkt.jump, sizeof(skip_pkt.jump));
	}
}*/


void app_mem_unsent_skip_data_read(uint8_t* pData, uint16_t size)
{
	save_skip_packet_t skip_pkt;

	if(sizeof_data_need_to_be_sent(&skip_index) > 0) {
		if(read_oldest_data(&skip_index, (uint8_t*)&skip_pkt, sizeof(skip_pkt), true)) {
			memcpy(pData, (uint8_t*)&skip_pkt.skip, sizeof(skip_pkt.skip));
		}
	}
}


void app_mem_sent_data_abandon(void)
{
	abandon_data_have_sent(&skip_index);
}


uint32_t app_mem_unsent_data_size_get(void)
{
	return sizeof_data_need_to_be_sent(&skip_index);
}


void app_mem_skip_data_save(void)
{
	save_skip_packet_t save_skip_pkt;

#if 1
	skip_packet_t *g_skip = &g_app_data.tmp.skip;

	APP_MEM_LOG("utc: %d.\n", g_skip->start_utc);
	APP_MEM_LOG("mode: %d.\n", g_skip->mode);
	APP_MEM_LOG("secs: %d.\n", g_skip->skip_sec_sum);
	APP_MEM_LOG("cnt: %d.\n", g_skip->skip_cnt_sum);
	APP_MEM_LOG("freq avg: %d.\n", g_skip->freq_avg);
	APP_MEM_LOG("freq max: %d.\n", g_skip->freq_max);
	APP_MEM_LOG("conti max: %d.\n", g_skip->consecutive_skip_max_num);
	for(uint8_t i=0;i<=g_skip->trip_num;i++) {
		APP_MEM_LOG("c_sec: %d.\n", g_skip->group[i].skip_sec);
		APP_MEM_LOG("c_cnt: %d.\n", g_skip->group[i].skip_cnt);
	}

	memcpy((uint8_t*)&save_skip_pkt.skip, (uint8_t*)g_skip, sizeof(skip_packet_t));
	pack_and_save_data(&skip_index, (uint8_t*)&save_skip_pkt, sizeof(save_skip_pkt));
#else
	skip_packet_t g_skip;
	static uint16_t offset = 1;
	g_skip.start_utc = 1608343471 + offset;
	g_skip.mode = 0;
	g_skip.setting = 80;
	g_skip.skip_sec_sum = 0;
	g_skip.skip_cnt_sum = 0;
	g_skip.freq_avg = 100;
	g_skip.freq_max = 123;
	g_skip.consecutive_skip_max_num = 111;
	g_skip.trip_num = 49;
	for(uint8_t i=0;i<=g_skip.trip_num;i++) {
		g_skip.group[i].skip_sec = offset + i;
		g_skip.group[i].skip_cnt = offset + i;
		g_skip.skip_sec_sum += g_skip.group[i].skip_sec;
		g_skip.skip_cnt_sum += g_skip.group[i].skip_cnt;
	}
	offset++;
	
	memcpy((uint8_t*)&save_skip_pkt.skip, (uint8_t*)&g_skip, sizeof(g_skip));
	pack_and_save_data(&skip_index, (uint8_t*)&save_skip_pkt, sizeof(save_skip_pkt));
#endif
}

void app_mem_data_init(void)
{
	init_valid_addr(&skip_index);
}


void app_store_mem_data_revert(void)
{
	uint32_t i;

	//Clear sport cache data
	for(i=0;i<((SKIP_DATA_SAVED_END_ADDR - TEMP_DATA_RST_ADDR)/FLASH_SECTOR_SIZE);i++) {
		hal_flash_erase_sector(TEMP_DATA_RST_ADDR + i*FLASH_SECTOR_SIZE);
		wdt_feed(0x3fff);
	}

	app_mem_data_init();
}


void app_mem_data_clear_all(void)
{
	uint32_t i;

	//Clear sport cache data
	for(i=0;i<((SKIP_DATA_SAVED_END_ADDR - SF_MEM_DATA_ADDR)/FLASH_SECTOR_SIZE);i++) {
		hal_flash_erase_sector(SF_MEM_DATA_ADDR + i*FLASH_SECTOR_SIZE);
		wdt_feed(0x3fff);
	}

	app_mem_data_init();
}

#if 0
void app_mem_store_test(void)
{
	skip_packet_t skip_pkt;

	//app_mem_data_clear_all();
	
	for(uint16_t i=0;i<1;i++) {
		app_mem_skip_data_save();
		wdt_feed(0x3fff);
	}
	
	/*uint32_t left_size = app_mem_unsent_data_size_get();
	APP_MEM_LOG("left size1: %d.\n", left_size);
	while(app_mem_unsent_data_size_get()) {
		app_mem_unsent_skip_data_read((uint8_t*)&skip_pkt, sizeof(skip_pkt));
		abandon_data_have_sent(&skip_index);
		APP_MEM_LOG("utc1: %d.\n", skip_pkt.start_utc);
		APP_MEM_LOG("mode1: %d.\n", skip_pkt.mode);
		APP_MEM_LOG("secs1: %d.\n", skip_pkt.used_sec);
		APP_MEM_LOG("cnt1: %d.\n", skip_pkt.jump_cnt);
		APP_MEM_LOG("freq avg1: %d.\n", skip_pkt.freq_avg);
		APP_MEM_LOG("freq max1: %d.\n", skip_pkt.freq_max);
		for(uint8_t i=0;i<skip_pkt.conti_valid_num;i++) {
			APP_MEM_LOG("c_sec1: %d.\n", skip_pkt.conti[i].secs);
			APP_MEM_LOG("c_cnt1: %d.\n", skip_pkt.conti[i].jump_cnt);
		}
		wdt_feed(0x3fff);
	}*/
	
	/*left_size = app_mem_unsent_data_size_get();
	APP_MEM_LOG("left size2: %d.\n", left_size);
	
	for(uint16_t i=0;i<200;i++) {
		app_mem_skip_data_save();
	}
	
	left_size = app_mem_unsent_data_size_get();
	APP_MEM_LOG("left size3: %d.\n", left_size);

	while(app_mem_unsent_data_size_get()) {
		app_mem_unsent_skip_data_read((uint8_t*)&skip_pkt, sizeof(skip_pkt));
		abandon_data_have_sent(&skip_index);
		APP_MEM_LOG("utc1: %d.\n", skip_pkt.start_utc);
		APP_MEM_LOG("mode1: %d.\n", skip_pkt.mode);
		APP_MEM_LOG("secs1: %d.\n", skip_pkt.used_sec);
		APP_MEM_LOG("cnt1: %d.\n", skip_pkt.jump_cnt);
		APP_MEM_LOG("freq avg1: %d.\n", skip_pkt.freq_avg);
		APP_MEM_LOG("freq max1: %d.\n", skip_pkt.freq_max);
		for(uint8_t i=0;i<skip_pkt.conti_valid_num;i++) {
			APP_MEM_LOG("c_sec1: %d.\n", skip_pkt.conti[i].secs);
			APP_MEM_LOG("c_cnt1: %d.\n", skip_pkt.conti[i].jump_cnt);
		}
	}*/
}
#endif
